package animationEngine;

import java.awt.Color;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import javax.swing.BoxLayout;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;

/**
 * The Class PseudoCodeFrame, this class represents the Frame containing the Pseudo Code for
 * the algorithm.  The pseudo code is loaded from a file in the projects pseudo code directory
 * 
 * @author Andrei Palade
 * @author David McBrierty
 * @author Garry Sharp
 * @author Hristo Georgiev
 * @author John Dennis
 * @author Marco Sarconi
 * @version 1.0.1
 */
public class PseudoCodeFrame extends JInternalFrame {

	/** Auto generated version ID. */
	private static final long serialVersionUID = 7893702641434384191L;
	
	/** Array storing the strings that are loaded in from the pseudo code file. */
	ArrayList<String> linesOfCode;
	
	/** Array storing the JLabels that will be displayed in this frame. */
	ArrayList<JLabel> displayLines;
	
	/** Index of the array that represents the currently highlighted line. */
	int highlightedLine = 0;
	
	/**
	 * Constructor.
	 *
	 * @throws FileNotFoundException the file not found exception
	 */
	public PseudoCodeFrame() throws FileNotFoundException{
		super("Pseudo Code", true, false, true ,true);
		
		//Initialise arrays storing the lines of code and the JLabels to display
		this.linesOfCode = new ArrayList<String>();
		this.displayLines = new ArrayList<JLabel>();
	    
		//Set this frames attributes
	    this.setLocation(350, 265);
	    this.setSize(500, 200);
	    this.setVisible(true);
	    
	    //Set layout
	    this.setLayout(new BoxLayout(this.getContentPane(), BoxLayout.Y_AXIS));	  
	    
	}

	
	/**
	 * This method loads the pseudo code in from the given file name.
	 *
	 * @param fileName The file name of the text file containing the pseudo code.
	 * @throws FileNotFoundException the file not found exception
	 */
	public PseudoCodeFrame(String fileName) throws FileNotFoundException {
		this();
		this.updateUI();
		
		String[] bf = {"WHILE the text has not been exhausted{",
					   "\tIF characters match{ ",
					   "\t\tIncrement current position in text;",
					   "\t\tIncrement current position in pattern;",
					   "\t}",
					   "\tELSE IF characters don't match{",
					   "\t\tStart again in string;",
					   "\t\tAdvance starting position;",
					   "\t\tBackup in text to new starting position;",
					   "\t}",
					   "\tIF current position in pattern = end of string{", 
					   "\t\tRETURN starting position;",
					   "\t}",
					   "\tELSE {", 
					   "\t\tRETURN not found;",
					   "\t}",
					   "}"};
		String[] bm = {"Set up the last occurrence array;",
					   "WHILE the text and string have not been exhausted {",
					   "\tIF characters match {",
					   "\t\tDecrement current position in text;",
					   "\t\tDecrement current position in pattern;",
				  	   "\t}",
				  	   "\tELSE { characters do not match",
				  	   "\t\tIF last position of character of text in string before current position {",
				  	   "\t\t\tMove string such that last position of char is aligned with current position in text",
				  	   "\t\t\tReturn to the end of string",
				       "\t\t}",
				       "\t\tELSE IF last position of character of text in string after current position {",
				       "\t\t\tMove string along by one place",
				       "\t\t\tReturn to end of the string",
				       "\t\t}",
				       "\t\tELSE character of text does not appear in the text",
				       "\t\t\tMove string past character in text",
				       "\t\t\tReturn to the end of the string",
				       "\t\t}",
				  	   "\t}",
					   "}",
					   "IF string was found in the text (string was exhausted)",
					   "\tRETURN starting position;",
					   "ELSE string was not found in text (text was exhausted)",
					   "\tRETURN -1;"};
		
		String[] kmp ={"Set up the border table;",
					   "WHILE the text has not been exhausted {",
					   "\tIF characters match {",
					   "\t\tIncrement current position in text;",
					   "\t\tIncrement current position in pattern;",
					   "\n",
					   "\t\tIF current position in pattern = end of string",
					   "\t\t\tRETURN current position in pattern - current position in string;", 
			  		   "\t}",
			  		   "\tELSE if characters don't match {",
			  		   "\t\tIF there is a common prefix/suffix {",
			  		   "\t\t\tChange position in string according to the border table entry;",
			    	   "\t\t}",
			    	   "\t\tELSE {",
			    	   "\t\t\tMove forward one position in text;",
			    	   "\t\t\tStart from beginning of the pattern;",
			    	   "\t\t}",
			  		   "\t}",
					   "}",
					   "RETURN -1;"};
		
		if(fileName.equalsIgnoreCase("BF")){
			for(String str: bf)
				linesOfCode.add(str);
		}
		
		else if(fileName.equalsIgnoreCase("BM")){
			for(String str: bm)
				linesOfCode.add(str);
		}
		else{
			for(String str: kmp)
				linesOfCode.add(str);
		}
		
		//Load in the pseudo code, construct the labels and highlight the first line
	    this.constructLabels();
	    this.highlightLine(true);
	}
	
	/**
	 * Creates the frame.
	 *
	 * @param filename the filename
	 * @throws FileNotFoundException the file not found exception
	 */
	public void createFrame(String filename) throws FileNotFoundException{
		new PseudoCodeFrame(filename);
	}
	/**
	 * This method constructs the JLabels from the array that stores the strings for each
	 * line of the pseudo code file.
	 */
	public void constructLabels(){
		for (String line : this.linesOfCode){
			//Add html tags to start and end of the string so that tabs are correctly displayed
			JLabel label = new JLabel("<html><pre>"+line+"</pre></html>");
			//Label must be set to opaque so that the background color can be changed
			label.setOpaque(true);
			//Add the newly constrcuted label to the array of labels
			this.displayLines.add(label);
			//And add it to this so it is displayed
			this.add(label);
		}
		this.setSize(500, (int) (2.5f * this.displayLines.get(0).getFont().getSize() * this.linesOfCode.size()));
	}
	
	/**
	 * Helper method to print out the contents of the linesOfCode array.
	 */
	//TODO Remove this method.
	public void printLines(){
		for (String element : this.linesOfCode){
			System.out.println(element);
		}
	}
	
	/**
	 * This method highlights the current line using the highLightedLine integer, the boolean
	 * represents the direction we are traveling in, true forwards, false for backwards.
	 *
	 * @param direction the direction
	 */
	public void highlightLine(boolean direction){
		//Clear the currently highlighted line first
		this.clearHighLightedLine();
		//Sleep for 1 millisecond to allow previous line time to clear.
		try {
			Thread.sleep(1);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		//Skip lines that don't contain enough characters to contain code,
		//this number includes the length of the html tags added to the strings
		while (this.displayLines.get(this.highlightedLine).getText().length() <= 30){
			
			//If we are going backwards
			if (!direction){
				//Decrement the line number
				this.highlightedLine--;
				//Check it hasnt fallen below 0
				if (this.highlightedLine < 0){
					this.highlightedLine = this.displayLines.size()-1;
					break;
				}
			}
			//Else we are going forwards
			else {
				//increment the line number
				this.highlightedLine++;
				
				//Check it isnt about the array size
				if (this.highlightedLine > this.displayLines.size()-1){
					this.highlightedLine = 0;
					break;
				}
			}
			
		}
		
		//Set the background colour of the line that is to be highlighted
		this.displayLines.get(this.highlightedLine).setBackground(Color.GREEN);
	}
	
	/**
	 * This method is used to clear the line before currently highlighted line.
	 */
	public void clearHighLightedLine(){
		if (this.highlightedLine > 0)
			this.displayLines.get(this.highlightedLine-1).setBackground(null);
		//This removes the highlight on the last line of code when we are back at the start 
		//of the algorithm
		else if (this.highlightedLine == 0)
			this.displayLines.get(this.displayLines.size()-1).setBackground(null);
	}
	
	/**
	 * Clear current line highlight.
	 */
	public void clearCurrentLineHighlight(){
		//System.out.println("Clearing line " + this.highlightedLine);
		this.displayLines.get(this.highlightedLine).setBackground(null);
	}
	
	/**
	 * This method increments the currently highlighted line integer and highlights it.
	 */
	public void incHighligtedLine(){
		this.highlightedLine++;
		//Check to see if we have exceed the number of lines in the array
		if (this.highlightedLine >= this.displayLines.size()-1){
			this.highlightedLine = 0;
		}
		//Call highlight line with true as we are going forwards
		this.highlightLine(true);
	}
	
	/**
	 * This method decrements the currently highlighted line integer and highlights it.
	 */
	public void decHighligtedLine(){
		this.clearCurrentLineHighlight();
		this.highlightedLine--;
		//Check to see if we have fallen below 0
		if (this.highlightedLine < 0){
			this.highlightedLine = this.displayLines.size()-1;
		}
		//Call highlight line with false as we are going backwards
		this.highlightLine(false);
	}
	
	/**
	 * This method is sued to highlight a given line number.
	 * 
	 * @param lineNumber The line number to highlight
	 */
	public void highlightLine(int lineNumber){
		this.clearCurrentLineHighlight();
		//Sleep for 1 millisecond to allow clear line to work
		try {
			Thread.sleep(1);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		this.displayLines.get(lineNumber).setBackground(Color.GREEN);
		//Update the highlighted line integer
		this.highlightedLine = lineNumber;
	}
	
	/**
	 * Reset.
	 */
	public void reset(){
		this.clearCurrentLineHighlight();
		this.highlightedLine = 0;
		this.highlightLine(true);
	}
	
	/**
	 * This method is used to rebuild the entire frame from the array of JLabels.
	 */
	//TODO Remove this method as it isnt used.
	public void reconstruct(){
		this.getContentPane().removeAll();
		this.setLayout(new BoxLayout(this.getContentPane(), BoxLayout.Y_AXIS));
		for (JLabel label : this.displayLines){
			this.add(label);
		}
		this.validate();
		//this.setOpaque(true);
		this.setLocation(350, 300);
	    this.setSize(500, 200);
	    this.setVisible(true);
	}
	

}
